module Hashish
  module PrivateClassMethods
    
    private
    def get_real_value(index, data)
      index.is_a?(Proc) ? index.call(data) : data[index]
    end

    def hashish_get_key(item)
      key_value = get_real_value(@key, item)
    end

    def hashish_remove_metadata(key, data)
      @indexes.each do |name, value|
        if (v = get_real_value(value, data)).is_a? Array
          v.each do |real_value|
            Hashish.redis_connection.zrem("#{@prefix}!#{name}=#{real_value}", "#{@prefix}@#{key}")
            Hashish.redis_connection.srem("#{@prefix}#INDEXES", "#{@prefix}!#{name}=#{real_value}")
          end
        else
          Hashish.redis_connection.zrem("#{@prefix}!#{name}=#{v}", "#{@prefix}@#{key}")
          Hashish.redis_connection.srem("#{@prefix}#INDEXES", "#{@prefix}!#{name}=#{v}")
        end
      end
      # Old slower code
      # Hashish.redis_connection.smembers("#{@prefix}#INDEXES").each do |member|
      #   Hashish.redis_connection.zrem(member, "#{@prefix}@#{key}")
      #   Hashish.redis_connection.srem("#{@prefix}#INDEXES", member) if Hashish.redis_connection.zcard(member) == 0
      # end

      # Old slower code
      Hashish.redis_connection.smembers("#{@prefix}#SORTERS").each do |member|
        if member =~ /^#{@prefix}@#{key}\$.+$/
          Hashish.redis_connection.del(member)
          Hashish.redis_connection.srem("#{@prefix}#SORTERS", member)
        end
      end
    end

    def hashish_add_metadata(name, category)
      Hashish.redis_connection.sadd("#{@prefix}##{category}", name)
    end

    def hashish_wait_on_lock(timeout = 0)
      lock_key = "#{@prefix}#MUTEX_LOCK"
      begin
        Timeout::timeout(timeout) do 
          while Hashish.redis_connection.incr(lock_key) != 1
            sleep(1)
          end
        end
        yield if block_given?
      ensure
        Hashish.redis_connection.del(lock_key)
      end
    end

    def hashish_redis_keys
      Hashish.redis_connection.keys("#{@prefix}*")
    end

    def hashish_single_insert(o, t) 
      raise "Data must be of type Hash or Array of Hash" unless o.is_a?(Hash)
      hashish_wait_on_lock do 
        data = o.to_json
        key = hashish_get_key(o)
        raise "Error: Computed data key as '#{key}'. Only alphanumeric characters and underscore allowed!" unless key.to_s =~ /^[\w_]+$/
        hashish_delete(key)
        Hashish.redis_connection.multi do 
          Hashish.redis_connection.zadd("#{@prefix}*", t , "#{@prefix}@#{key}")
          @indexes.each do |index_field, index|
            index_value = get_real_value(index, o)
            if index_value.is_a?(Array)
              index_value.each do |i|
                if i.is_a?(String) and !i.empty?
                  Hashish.redis_connection.zadd("#{@prefix}!#{index_field}=#{i}", t, "#{@prefix}@#{key}") 
                  hashish_add_metadata("#{@prefix}!#{index_field}=#{i}", 'INDEXES')
                end
              end
            elsif index_value.is_a?(String) and !index_value.empty?
              Hashish.redis_connection.zadd("#{@prefix}!#{index_field}=#{index_value}", t, "#{@prefix}@#{key}")
              hashish_add_metadata("#{@prefix}!#{index_field}=#{index_value}", 'INDEXES')
            end
          end
          @sorters.each do |sort_field, sort|
            sort_value = sort.is_a?(Proc) ? sort.call(o) : o[sort]
            if sort_value # only if there is a sort_value
              Hashish.redis_connection.set("#{@prefix}@#{key}$#{sort_field}", sort_value)
              hashish_add_metadata("#{@prefix}@#{key}$#{sort_field}", 'SORTERS')
            end
          end
          Hashish.redis_connection.set("#{@prefix}@#{key}", data)
        end
      end
      true
    end

  end
end
